# RVC Controller SW — Software Design Document (SDD)

| 항목 | 내용 |
|------|------|
| 문서 번호 | RVC-SDD-001 |
| 버전 | v1.0 |
| 작성일 | 2026-05-20 |
| 작성자 | OOAD Team |
| 상태 | 확정 (Released) |

---

## 변경 이력 (Change History)

| 버전 | 날짜 | 변경 내용 | 작성자 |
|------|------|-----------|--------|
| v1.0 | 2026-05-20 | 최초 작성 — 구현 완료 기준 아키텍처·클래스·인터페이스·Simulator·설계 결정 통합 | OOAD Team |

---

## 참조 문서 (Reference Documents)

| 문서 번호 | 문서명 | 비고 |
|-----------|--------|------|
| RVC-SRS-001 | System Requirements Specification | 요구사항 출처 |
| RVC-DM-001 | Domain Model | 도메인 개념 클래스 기반 |
| RVC-CD-001 | Design Class Diagram | 본 문서에서 구현 기준으로 갱신 |
| RVC-SSD-001 | System Sequence Diagrams | SSD-01~08 |
| RVC-SD-001 | Sequence Diagrams (Design Level) | SD-01~08 |
| RVC-UC-001 | Use Case Specification | UC-01~08 |

---

## 1. 서론 (Introduction)

### 1.1 목적

본 문서는 RVC Controller SW의 **소프트웨어 설계**를 기술한다.  
아키텍처 결정, 클래스 설계, 인터페이스 계약, Simulator 설계, 행위 설계(Sequence Diagram 요약)를 포함하며, 구현 완료 시점을 기준으로 작성된 **As-Built SDD**이다.

### 1.2 설계 접근 방식

| 단계 | 입력 | 출력 |
|------|------|------|
| OOA (분석) | FR/NFR, Use Cases | Domain Model, SSD, System Operations |
| OOD (설계) | Domain Model + System Operations | Design Class Diagram, Sequence Diagrams (Design) |
| OOP (구현) | Design Class Diagram + SD | C++17 소스 코드, 테스트 |

---

## 2. 아키텍처 설계 (Architectural Design)

### 2.1 계층 구조 (Layered Architecture)

```
┌────────────────────────────────────────────────────┐
│  Layer 3 — Public API                              │
│  RVC  (startCleaning, getState)                    │
├────────────────────────────────────────────────────┤
│  Layer 2 — Control Logic                           │
│  Controller  (pollSensors, avoidObstacle, boost…)  │
├────────────────────────────────────────────────────┤
│  Layer 1 — HW Abstraction Interfaces               │
│  MovementUnit │ CleaningUnit │ Sensor               │
│  (pure virtual — 플랫폼 독립적 계약)               │
├────────────────────────────────────────────────────┤
│  Layer 0 — Concrete Implementations                │
│  [HW Driver]  │  [Simulator]  │  [Mock (Test)]     │
└────────────────────────────────────────────────────┘
```

| 계층 | 역할 | 의존 방향 |
|------|------|-----------|
| Public API (RVC) | 외부 진입점. Controller에 위임 | ↓ Controller |
| Control Logic (Controller) | 모든 UC 로직 구현. 상태 관리 | ↓ Layer 1 인터페이스 |
| HW Abstraction (Interface) | 플랫폼 독립적 계약 정의 (= 0) | 없음 (최하위 계약) |
| Concrete (HW/Sim/Mock) | 실제 구현체. 계층을 교체해도 Controller 불변 | ↑ Layer 1 구현 |

### 2.2 컴포넌트 다이어그램

```
              ┌─────────────────────────────────┐
              │         rvc_lib                 │
              │  RVC.hpp/.cpp                   │
              │  Controller.hpp/.cpp            │
              │  ── 추상 인터페이스 ──           │
              │  Sensor / ObstacleSensor /      │
              │  DustSensor / MovementUnit /    │
              │  CleaningUnit / Types           │
              └──────────────┬──────────────────┘
                             │ links
          ┌──────────────────┼──────────────────┐
          │                  │                  │
 ┌────────┴────────┐  ┌──────┴───────┐  ┌──────┴──────────┐
 │  simulator_lib  │  │  unit_tests  │  │integration_tests│
 │  SimObstacleSensor│  MockSensors  │  │  MockSensors    │
 │  SimDustSensor  │  │ControllerTest│  │ RVCSystemTest   │
 │  SimMovementUnit│  │ SensorTest   │  └─────────────────┘
 │  SimCleaningUnit│  └──────────────┘
 │  RVCSimulator   │
 └────────┬────────┘
          │ links
 ┌────────┴────────┐  ┌─────────────────┐
 │    rvc_sim      │  │  system_tests   │
 │  (CLI 실행파일) │  │RVCSimulatorTest │
 └─────────────────┘  └─────────────────┘
```

### 2.3 핵심 설계 원칙 적용

| 원칙 | 적용 내용 | 코드 근거 |
|------|-----------|-----------|
| **DIP** (의존성 역전) | Controller는 구체 클래스가 아닌 추상 인터페이스에 의존 | 생성자 파라미터 타입이 모두 추상 클래스 포인터 |
| **OCP** (개방-폐쇄) | 새 센서 추가 시 `Sensor`를 상속, Controller 코드 불변 | `Sensor` 추상 기반 클래스 |
| **SRP** (단일 책임) | Controller = 제어 로직만, MovementUnit = 이동만, CleaningUnit = 청소만 | 클래스별 책임 분리 |
| **ISP** (인터페이스 분리) | ObstacleSensor / DustSensor 인터페이스 분리 | 별도 헤더 파일 |
| **DI** (의존성 주입) | 생성자 주입 (Constructor Injection) | `Controller(MovementUnit*, CleaningUnit*, …)` |

---

## 3. 데이터 설계 (Data Design)

### 3.1 열거형 (Types.hpp)

| 열거형 | 기반 타입 | 값 | 사용 클래스 |
|--------|-----------|-----|------------|
| `RVCState` | `uint8_t` | `IDLE, CLEANING, STOPPED, TURNING, REVERSING` | Controller, RVC |
| `Direction` | `uint8_t` | `FRONT, LEFT, RIGHT, BACK` | MovementUnit, Controller |
| `PowerLevel` | `uint8_t` | `NORMAL, BOOSTED` | CleaningUnit |
| `SensorStatus` | `uint8_t` | `OK, ERROR_TRANSIENT, ERROR_PERSISTENT` | Sensor |
| `SensorPosition` | `uint8_t` | `FRONT, LEFT, RIGHT` | ObstacleSensor, Controller |

> `uint8_t` 기반 타입 사용 이유: `performance-enum-size` 정적 분석 규칙 준수 (4바이트 → 1바이트)

### 3.2 Controller 내부 상태

| 멤버 | 타입 | 초기값 | 역할 |
|------|------|--------|------|
| `state_` | `RVCState` | `IDLE` | 현재 동작 상태 |
| `frontObstacle_` | `bool` | `false` | 전방 장애물 캐시 |
| `leftObstacle_` | `bool` | `false` | 좌측 장애물 캐시 |
| `rightObstacle_` | `bool` | `false` | 우측 장애물 캐시 |
| `lastDustLevel_` | `double` | `0.0` | 마지막 먼지 레벨 캐시 |
| `sensorErrorCount_` | `unordered_map<int,int>` | `{}` | 센서 ID별 오류 누적 카운터 |
| `kErrorThreshold` | `static constexpr int` | `3` | 영구 오류 판정 임계값 |
| `kDustThreshold` | `static constexpr double` | `0.5` | 부스트 발동 임계값 |

---

## 4. 클래스 설계 (Class Design)

### 4.1 설계 클래스 다이어그램 (구현 기준 최종본)

> Domain Model 대비 변경: 메서드 시그니처 확정, 추상 인터페이스 분리, 구현에서 발생한 타입 조정 반영

```mermaid
---
title: "Design Class Diagram — RVC Controller SW (As-Built v1.0)"
---
classDiagram
    direction TB

    class RVCState {
        <<enumeration>>
        IDLE
        CLEANING
        STOPPED
        TURNING
        REVERSING
    }
    class Direction {
        <<enumeration>>
        FRONT
        LEFT
        RIGHT
        BACK
    }
    class PowerLevel {
        <<enumeration>>
        NORMAL
        BOOSTED
    }
    class SensorStatus {
        <<enumeration>>
        OK
        ERROR_TRANSIENT
        ERROR_PERSISTENT
    }
    class SensorPosition {
        <<enumeration>>
        FRONT
        LEFT
        RIGHT
    }

    class RVC {
        -controller_ : Controller*
        -state_ : RVCState
        +RVC(controller : Controller*)
        +startCleaning() void
        +getState() RVCState
    }

    class Controller {
        +kErrorThreshold : int = 3
        +kDustThreshold : double = 0.5
        +Controller(mu, cu, front, left, right, dust)
        +startCleaning() void
        +reportObstacle(position : SensorPosition) void
        +reportDust(dustLevel : double) void
        +reportSensorError(sensorId : int) void
        +pollSensors() void
        -avoidFrontObstacle() void
        -avoidAllDirectionObstacle() void
        -boostCleaningPower() void
        -checkSideObstacles() void
        -recordObstacleDetection(position : SensorPosition) void
        -recordDustDetection(dustLevel : double) void
        -validateSensorData(sensorId : int) void
        -retainLastValidState() void
        -state_ : RVCState
        -sensorErrorCount_ : map~int,int~
    }

    class Sensor {
        <<abstract>>
        +getStatus() SensorStatus
        +getSensorId() int
        +readData() void
    }

    class ObstacleSensor {
        <<abstract>>
        +isObstacleDetected() bool
        +getPosition() SensorPosition
    }

    class DustSensor {
        <<abstract>>
        +isDustDetected() bool
        +getDustLevel() double
    }

    class MovementUnit {
        <<abstract>>
        +moveForward() void
        +moveBackward() void
        +stop() void
        +turn(direction : Direction) void
        +getDirection() Direction
    }

    class CleaningUnit {
        <<abstract>>
        +startCleaning() void
        +setPowerLevel(level : PowerLevel) void
        +boostPower() void
        +restoreNormalPower() void
        +getIsActive() bool
    }

    RVC "1" --> "1" Controller : delegates
    Controller "1" --> "1" MovementUnit : commands
    Controller "1" --> "1" CleaningUnit : commands
    Controller "1" --> "3" ObstacleSensor : front·left·right
    Controller "1" --> "1" DustSensor : monitors
    Sensor <|-- ObstacleSensor
    Sensor <|-- DustSensor

    RVC ..> RVCState
    Controller ..> RVCState
    Controller ..> SensorPosition
    MovementUnit ..> Direction
    CleaningUnit ..> PowerLevel
    Sensor ..> SensorStatus
    ObstacleSensor ..> SensorPosition
```

### 4.2 Domain Model → Design Class Diagram 주요 변경

| 항목 | Domain Model | Design (As-Built) | 변경 이유 |
|------|-------------|-------------------|-----------|
| `Direction` 값 | FORWARD, BACKWARD, LEFT, RIGHT | FRONT, LEFT, RIGHT, BACK | `FRONT` = 직진 방향, `BACK` = 후방 회전을 표현 |
| `RVCState` 값 | CLEANING, STOPPED, TURNING, REVERSING | + `IDLE` 추가 | 초기 미시작 상태 필요 |
| `SensorStatus` 값 | ACTIVE, ERROR | OK, ERROR_TRANSIENT, ERROR_PERSISTENT | 오류 심각도 구분 (일시적/영구적) |
| `sensorId` 타입 | String | int | C++ 맵 키, 정수 ID가 더 효율적 |
| `checkSideObstacles()` 반환 | SensorPosition | void | 좌·우 결과를 멤버 변수(`leftObstacle_`, `rightObstacle_`)에 캐시 |
| `validateSensorData()` 반환 | boolean | void | 결과를 직접 분기 처리, 반환값 불필요 |
| `boostPower()` 반환 | PowerLevel | void | Controller가 반환값을 사용하지 않음 |
| `Obstacle`, `Dust` 클래스 | 환경 개념 클래스 | 제거 | 센서 클래스 내부 상태로 흡수 |
| `pollSensors()` 가시성 | private | **public** | 단계별 테스트(TDD)에서 직접 호출 필요 |
| `[[nodiscard]]` | 없음 | 모든 non-void getter에 적용 | `modernize-use-nodiscard` 정적 분석 규칙 |

---

## 5. 인터페이스 설계 (Interface Design)

### 5.1 Sensor (추상 기반)

```cpp
// Sensor.hpp — 모든 센서의 공통 계약
class Sensor {
public:
    virtual ~Sensor() = default;
    [[nodiscard]] virtual SensorStatus getStatus()   const = 0;
    [[nodiscard]] virtual int          getSensorId() const = 0;
    virtual void                       readData()          = 0;
};
```

### 5.2 ObstacleSensor

```cpp
// ObstacleSensor.hpp — Sensor 상속
class ObstacleSensor : public Sensor {
public:
    [[nodiscard]] virtual bool           isObstacleDetected() const = 0;
    [[nodiscard]] virtual SensorPosition getPosition()        const = 0;
};
```

### 5.3 DustSensor

```cpp
// DustSensor.hpp — Sensor 상속
class DustSensor : public Sensor {
public:
    [[nodiscard]] virtual bool   isDustDetected() const = 0;
    [[nodiscard]] virtual double getDustLevel()   const = 0;
};
```

### 5.4 MovementUnit

```cpp
// MovementUnit.hpp
class MovementUnit {
public:
    virtual ~MovementUnit() = default;
    virtual void              moveForward()             = 0;
    virtual void              moveBackward()            = 0;
    virtual void              stop()                    = 0;
    virtual void              turn(Direction direction) = 0;
    [[nodiscard]] virtual Direction getDirection() const = 0;
};
```

### 5.5 CleaningUnit

```cpp
// CleaningUnit.hpp
class CleaningUnit {
public:
    virtual ~CleaningUnit() = default;
    virtual void startCleaning()                 = 0;
    virtual void setPowerLevel(PowerLevel level) = 0;
    virtual void boostPower()                    = 0;
    virtual void restoreNormalPower()            = 0;
    [[nodiscard]] virtual bool getIsActive() const = 0;
};
```

### 5.6 인터페이스 설계 원칙 요약

| 원칙 | 적용 |
|------|------|
| 순수 가상 (`= 0`) | HW 접근 메서드 전부 |
| 가상 소멸자 (`= default`) | 다형적 삭제 지원 |
| `[[nodiscard]]` | 모든 const getter (컴파일 시점 미사용 반환값 경고) |
| `cppcoreguidelines-special-member-functions` 제외 | 추상 인터페이스는 Rule-of-Five 불필요 |

---

## 6. 컴포넌트 상세 설계 (Component Design)

### 6.1 RVC

| 항목 | 내용 |
|------|------|
| 파일 | `src/rvc/RVC.hpp`, `RVC.cpp` |
| 역할 | Public API 진입점. Controller를 소유하지 않고 포인터로 참조 |
| 책임 | `startCleaning()` → `controller_->startCleaning()` 위임 |
| 상태 | `state_` (IDLE → CLEANING) |

### 6.2 Controller

| 항목 | 내용 |
|------|------|
| 파일 | `src/rvc/Controller.hpp`, `Controller.cpp` |
| 역할 | 모든 UC 로직 구현체. 시스템의 두뇌 |
| 생성 | 생성자 주입 — 6개 포인터(MovementUnit, CleaningUnit, 3×ObstacleSensor, DustSensor) |
| 공개 메서드 | `startCleaning()`, `reportObstacle()`, `reportDust()`, `reportSensorError()`, `pollSensors()` |
| 핵심 비공개 흐름 | `avoidFrontObstacle()` → `checkSideObstacles()` → 분기 → `avoidAllDirectionObstacle()` |

**pollSensors() 흐름 (SD-01 구현):**

```
pollSensors()
  ├─ frontSensor_->readData()
  ├─ if isObstacleDetected() → avoidFrontObstacle() → return
  ├─ dustSensor_->readData()
  ├─ if getDustLevel() > kDustThreshold → boostCleaningPower()
  └─ movementUnit_->moveForward()
```

**avoidFrontObstacle() 흐름 (SD-03 구현):**

```
avoidFrontObstacle()
  ├─ stop()
  ├─ checkSideObstacles()   ← leftSensor, rightSensor readData() + cache
  ├─ if !leftObstacle_  → turn(LEFT)  → moveForward()
  ├─ elif !rightObstacle_ → turn(RIGHT) → moveForward()
  └─ else → avoidAllDirectionObstacle()
```

**validateSensorData() 흐름 (SD-08 구현):**

```
validateSensorData(sensorId)
  ├─ sensorErrorCount_[sensorId]++
  ├─ if count >= kErrorThreshold → movementUnit_->stop()
  └─ else → retainLastValidState()  // no-op
```

### 6.3 Simulator 컴포넌트

#### 6.3.1 Simulator 클래스 다이어그램

```mermaid
---
title: "Simulator Design — rvc::sim namespace"
---
classDiagram
    direction TB

    class SimObstacleSensor {
        +setObstacle(detected : bool) void
        +isObstacleDetected() bool
        +getPosition() SensorPosition
        +readData() void
        +getStatus() SensorStatus
        +getSensorId() int
        -position_ : SensorPosition
        -id_ : int
        -obstacle_ : bool
    }

    class SimDustSensor {
        +setDustLevel(level : double) void
        +isDustDetected() bool
        +getDustLevel() double
        +readData() void
        +getStatus() SensorStatus
        +getSensorId() int
        -dustLevel_ : double
    }

    class SimMovementUnit {
        <<Motion: IDLE|FORWARD|BACKWARD|STOPPED|TURNING>>
        +moveForward() void
        +moveBackward() void
        +stop() void
        +turn(direction : Direction) void
        +getDirection() Direction
        +getMotion() Motion
        +getLastTurn() Direction
        +getStopCount() int
        +resetStopCount() void
        -motion_ : Motion
        -lastTurn_ : Direction
        -stopCount_ : int
    }

    class SimCleaningUnit {
        +startCleaning() void
        +setPowerLevel(level : PowerLevel) void
        +boostPower() void
        +restoreNormalPower() void
        +getIsActive() bool
        +getPowerLevel() PowerLevel
        +wasBoosted() bool
        +resetBoostFlag() void
        -active_ : bool
        -powerLevel_ : PowerLevel
        -boosted_ : bool
    }

    class RVCSimulator {
        +RVCSimulator()
        +start() void
        +tick() void
        +reportSensorError(sensorId : int) void
        +setFrontObstacle(detected : bool) void
        +setLeftObstacle(detected : bool) void
        +setRightObstacle(detected : bool) void
        +setDustLevel(level : double) void
        +movement() SimMovementUnit&
        +cleaning() SimCleaningUnit&
        -frontSensor_ : SimObstacleSensor
        -leftSensor_ : SimObstacleSensor
        -rightSensor_ : SimObstacleSensor
        -dustSensor_ : SimDustSensor
        -movementUnit_ : SimMovementUnit
        -cleaningUnit_ : SimCleaningUnit
        -controller_ : Controller
        -rvc_ : RVC
    }

    ObstacleSensor <|-- SimObstacleSensor
    DustSensor     <|-- SimDustSensor
    MovementUnit   <|-- SimMovementUnit
    CleaningUnit   <|-- SimCleaningUnit

    RVCSimulator *-- SimObstacleSensor : 3개
    RVCSimulator *-- SimDustSensor
    RVCSimulator *-- SimMovementUnit
    RVCSimulator *-- SimCleaningUnit
    RVCSimulator *-- Controller
    RVCSimulator *-- RVC
```

#### 6.3.2 Simulator 설계 결정

| 결정 | 내용 |
|------|------|
| `readData()` = no-op | 상태는 외부(`setObstacle`, `setDustLevel`)에서 주입. 폴링 사이클에서 HW 읽기 대신 캐시 반환 |
| `RVCSimulator`가 모두 소유 | Sim 컴포넌트 + Controller + RVC를 값 멤버로 보유. 포인터 무효화 방지를 위해 선언 순서 = 초기화 순서 |
| `wasBoosted()` / `resetBoostFlag()` | 부스트는 한 틱 안에서 boost→restore가 완료되므로 별도 flag로 추적 |
| `stopCount_` 누적 | stop()이 한 tick에 여러 번 호출될 수 있음(avoidFront + avoidAllDir). 횟수로 UC-05 검증 |

---

## 7. 행위 설계 (Behavioral Design)

### 7.1 SSD → SD 매핑 요약

| SSD | SD | 핵심 전환 |
|-----|----|-----------|
| SSD-01 | SD-01 | 블랙박스 `pollSensors()` → 내부 객체 분해 |
| SSD-02 | SD-02 | `reportObstacle(FRONT)` → `recordObstacleDetection` |
| SSD-03 | SD-03 | `avoidFrontObstacle()` self-call → `stop`, `checkSide`, `turn`, `forward` |
| SSD-04 | SD-04 | `reportObstacle(LEFT/RIGHT)` → 캐시 기록 |
| SSD-05 | SD-05 | `avoidAllDirectionObstacle()` → `stop`, `backward`, `turn(BACK)`, `forward` |
| SSD-06 | SD-06 | `reportDust(level)` → `recordDustDetection` |
| SSD-07 | SD-07 | `boostCleaningPower()` → `boostPower`, `restoreNormalPower` |
| SSD-08 | SD-08 | `reportSensorError(id)` → 카운터 증가 → stop 또는 retain |

> 각 SD 상세 다이어그램 → [sequence_diagram.md](sequence_diagram.md) 참조

### 7.2 Controller 상태 전이 (State Transition)

```
          startCleaning()
  IDLE ─────────────────────▶ CLEANING ──── pollSensors() ────▶ CLEANING
                                 │                                 ▲
                     frontObstacle │                               │
                                 ▼                                 │
                              STOPPED                              │
                                 │                                 │
                    checkSideObstacles()                           │
                         ┌───────┴───────┐                        │
                    !left │               │ !right                 │
                         ▼               ▼                        │
                       TURNING         TURNING                    │
                         │               │                        │
                  turn(LEFT)         turn(RIGHT)                  │
                         └───────┬───────┘                        │
                                 ▼                                 │
                              CLEANING ──────────────────────────┘
                                 │ both blocked
                                 ▼
                              STOPPED
                                 │ avoidAllDir
                                 ▼
                            REVERSING (backward)
                                 │
                                 ▼
                            TURNING (turn BACK)
                                 │
                                 ▼
                            CLEANING (forward)
```

---

## 8. 설계 결정 및 근거 (Design Decisions & Rationale)

### DD-01: 추상 인터페이스 + 생성자 주입

| 항목 | 내용 |
|------|------|
| **결정** | HW 의존성을 모두 순수 가상 클래스로 추상화, Controller 생성자에서 포인터 주입 |
| **대안** | Controller 내부에서 직접 HW 객체 생성 |
| **선택 이유** | HW 없이 Mock/Sim으로 테스트 가능. 플랫폼 교체 시 Controller 코드 변경 없음 (NFR-PRT-01, NFR-TST-02) |
| **트레이드오프** | 생성자 파라미터 6개 — 다소 장황하나 명시적 의존성이 은닉 의존성보다 안전 |

### DD-02: pollSensors()를 public으로 공개

| 항목 | 내용 |
|------|------|
| **결정** | `pollSensors()`를 public 메서드로 노출 |
| **대안** | private으로 유지, HW 타이머 인터럽트로만 호출 |
| **선택 이유** | TDD에서 단계별로 한 사이클씩 실행하려면 테스트 코드가 직접 호출해야 함 (NFR-TST-01) |
| **트레이드오프** | 미세한 캡슐화 약화 — TDD 가능성이 더 중요하다고 판단 |

### DD-03: 오류 임계값(kErrorThreshold) 방식

| 항목 | 내용 |
|------|------|
| **결정** | 단일 오류는 무시(retainLastValidState), 3회 누적 시 stop() |
| **대안** | 첫 오류에서 즉시 stop |
| **선택 이유** | 일시적 센서 노이즈에 강건 (NFR-SAF-02, NFR-REL-01) |
| **트레이드오프** | 2회 연속 오류는 계속 운행 — 허용 가능한 위험으로 판단 |

### DD-04: Sim 컴포넌트 값 멤버 소유

| 항목 | 내용 |
|------|------|
| **결정** | `RVCSimulator`가 모든 구성 요소를 값 멤버로 보유 |
| **대안** | `unique_ptr<T>` 사용 |
| **선택 이유** | 값 멤버로 선언 순서 = 초기화 순서 = 소멸 역순이 보장되어 포인터 생존 안전성 확보 |
| **트레이드오프** | Move 연산 지원 불가 (reset은 `make_unique<RVCSimulator>()` 재할당으로 해결) |

### DD-05: boostPower() / restoreNormalPower() 반환 타입을 void로

| 항목 | 내용 |
|------|------|
| **결정** | 원래 설계의 `PowerLevel` 반환 → `void`로 변경 |
| **이유** | Controller가 반환값을 사용하지 않음. `[[nodiscard]]` 규칙과 충돌 방지 |
| **영향** | MockSensors.hpp 및 관련 테스트 ON_CALL 코드 수정 |

---

## 9. 구현 기준 설계 요약 (Design Summary)

| 구성 요소 | 파일 수 | 클래스 수 | 핵심 역할 |
|-----------|---------|----------|-----------|
| 생산 코드 (`src/rvc/`) | 10 | 7 클래스 + 5 열거형 | Controller SW 핵심 |
| Simulator (`src/simulator/`) | 6 | 5 클래스 | HW-free 구체 구현체 |
| Mock (`tests/unit/mocks/`) | 1 | 4 Mock 클래스 | GMock 기반 테스트용 |
| 테스트 | 3 | — | Unit 33 + Integration 6 + System 9 = **53개** |

| 설계 원칙 | 준수 방법 |
|-----------|-----------|
| DIP | 추상 인터페이스 + 생성자 주입 |
| OCP | Sensor 상속 계층 — 기존 코드 무수정 확장 |
| SRP | 클래스 1개 = 책임 1개 |
| 이식성 | HW 추상화 계층 — Controller가 직접 HW 접근 없음 |
| 테스트 가능성 | Mock (단위/통합) + Simulator (시스템) + CLI (수동) |